Промотор сигма-фактора 54 РНК-полимеразы эубактерий - участок ДНК, c которого инициируется транскрипция после посадки на него сигма-фактора. Консенсус: 5'-TTGGCACGN4-TTGC-3'. Сигнал адресован бактериальной РНК-полимеразе.
Сначала, при сканировании ДНК РНК-полимеразой, альфа-субъединица узнает небольшой участок промотора. Затем привлекается сигма-субъединица (сигма-фактор 54) и специфично связывается с промотором, после чего с образованием закрытого комплекса (термин "закрытый" означает, что ДНК находится в двуцепочечном состоянии) и инициируется транскрипция. Сигнал является достаточно высокоэффективным.
Источники:
В этом практикуме использован скрипт Дмитрия Звездина.
import numpy as np
import pandas as pd
import requests
from scipy.stats import mannwhitneyu
from IPython.display import Image
genes_table = pd.read_csv("human-genes.tsv", sep="\t")
sequences = []
i = 300
req_count = 0
while req_count < 100:
chromosome = genes_table.loc[i, "#chrom"]
start = genes_table.loc[i, "thickStart"]
strand = genes_table.loc[i, "strand"]
i += 1
if str(strand) == "+":
link = f"https://rest.ensembl.org/sequence/region/human/{chromosome}:{start-6}..{start+400}:1?expand_3prime=0;expand_5prime=0"
request = requests.get(link, headers={ "Content-Type" : "text/x-fasta"})
else:
continue
if "error" in request.text:
continue
else:
req_count += 1
sequence = "".join(request.text.split("\n")[1:])
sequences.append(sequence)
with open("sequences.txt", "w") as file:
print(*sequences, sep="\n", file=file)
С помощью вышеприведенного скрипта было выделено 100 частичных последовательностей генов. Каждая последовательность содержит в себе 7 нуклеотидов до старт-кодона ATG, сам ATG и 397 нуклеотидов после него.
sequences_clear=[]
sequences_test_negative=[]
with open("sequences.txt", "r") as file:
sequences = file.readlines()
for sequence in sequences:
sequence_ = sequence[:13]
if sequence_[7:10] == "ATG":
sequences_clear.append(sequence_)
ATG_coord_fake = sequence[200:].find("ATG")
sequences_test_negative.append(sequence[200:][ATG_coord_fake-7:ATG_coord_fake+6])
sequences_train = sequences_clear[:40]
sequences_test = sequences_clear[40:]
sequences_test_negative = [seq for seq in sequences_test_negative if len(seq) == 13]
sequences_test_negative = sequences_test_negative[:36]
Из полученных последовательностей были вырезаны куски длины 13 нуклеотидов: 7 нуклеотидов до координаты старт-кодона ATG, сам старт-кодон и 6 нуклеотидов после него. Таким образом была создана выборка последовательностей, предположительно содержащих последовательность Козак, для построения PWM.
В качестве негативного контроля были вырезаны участки генов, отстоящие минимум на 200 нуклеотидов от начала последовательности и содержащие кодон ATG, не являющийся старт-кодоном. Они также содержат 7 нуклеотидов до координаты старт-кодона ATG, сам старт-кодон и 6 нуклеотидов после него.
class PWM():
def __init__(self, e=1, freq=[0.298, 0.298, 0.205, 0.205]):
self.e = e
self.positions = {"A": 0, "T": 1, "G": 2, "C": 3}
self.frequencies = np.array(freq).reshape((4, 1))
def fit(self, seq_list):
self.seq_arr = np.array(list(map(list, seq_list)), dtype="str")
self.n_pos = len(seq_list[0])
self.n_seq = len(seq_list)
A_n = np.count_nonzero(self.seq_arr == "A", axis=0)
T_n = np.count_nonzero(self.seq_arr == "T", axis=0)
G_n = np.count_nonzero(self.seq_arr == "G", axis=0)
C_n = np.count_nonzero(self.seq_arr == "C", axis=0)
self.matrix = np.stack([A_n, T_n, G_n, C_n], axis=0).astype("float")
pseudocount = self.e / 4
self.matrix += pseudocount
self.matrix = self.matrix / (self.n_seq+self.e)
self.pwm_matrix = np.log(self.matrix / self.frequencies)
def test(self, seq):
weight = 0
for i, nuc in enumerate(list(seq)):
weight += self.pwm_matrix[self.positions[nuc], i]
return weight
def ic_calculate(self):
self.ic_matrix = self.matrix * np.log2(self.matrix / self.frequencies)
def return_pwm(self):
return pd.DataFrame(self.pwm_matrix, columns = np.arange(1, self.n_pos + 1), index = ["A", "T", "G", "C"])
def return_ic(self):
return pd.DataFrame(self.ic_matrix, columns = np.arange(1, self.n_pos + 1), index = ["A", "T", "G", "C"])
В приведенном выше коде позиционная весовая матрица построена как класс с методами, вычисляющими веса тестовых последовательностей и матрицу информационного содержания. Базовый GC-состав составляет 41% (ссылка на источник).
pwm = PWM()
pwm.fit(sequences_train)
real_scores = []
fake_scores = []
for seq, fake_seq in zip(sequences_test, sequences_test_negative):
real_scores.append(pwm.test(seq))
fake_scores.append(pwm.test(fake_seq))
pwm.return_pwm()
print(f"Средний вес положительного контроля: {np.mean(real_scores)}, средний вес отрицательного контроля: {np.mean(fake_scores)}.")
print(mannwhitneyu(real_scores, fake_scores))
После построения PWM она была протестирована на тестовой выборке и негативном контроле (по 36 последовательностей). Принадлеженость вычисленных весов последовательностей для тестовой выборки и отрицательного контроля к одному распределению проверялась с помощью непараметрического теста Манна-Уитни.
При пороге значимости p = 0.05 гипотеза о принадлежности наборов весов одному распределению была отвергнута (p = 0.015). Веса последовательностей из тестового набора значимо выше весов последовательностей негативного контроля.
pwm.ic_calculate()
pwm.return_ic()
Выше представлена матрица информационного содержания последовательностей, использованных для построения PWM. На рисунке ниже показан LOGO.
Image(filename="logo.png")